długość / słownik |
stała-do-zmiennej | zmienna-do-zmiennej |
---|---|---|
kodowanie różnicowe | kompresja powtórzeń | |
stały słownik | kodowanie statystyczne | kompresja składowych |
zmienny słownik | sekwencyjne kodowanie statystyczne | algorytm Ziva i Lempela |
0101001 0101010 1001001 1101001 1011101 1000000
- linia n0101000 0101011 0111001 1100101 1011101 0000000
- linia n + 10000001 0000001 1110000 0001100 0000000 1000000
- różnica(7,1), (7,4), (8, 2), (10, 1)
- zakodowana różnicaŻadne słowo $c_i \in B^{*}$ nie jest prefiksem innego słowa $c_j \in B^{*}$, dla $i \neq j$.
Uwaga: każdy kod nie posiadający tej własności, może zostać zastąpiony kodem prefiksowym, bez utraty stopnia kompresji.
$\sum_{c_i \in \Gamma} r^{-|c_i|} \leq 1$
$|h(text)| = \sum_{a \in \Sigma} n_a \times |h(a)|$
$|h(text)| = \sum_{a \in \Sigma} n_a \times |depth(f_a)|$
gdzie:
def huffman(letter_counts):
nodes = []
for a, weight in letter_counts.items():
nodes.append(Node(a, weight))
internal_nodes = []
leafs = sorted(nodes, key=lambda n: n.weight)
while(len(leafs) + len(internal_nodes) > 1):
element_1, element_2 = # elementy nodes i internal nodes o najniższym koszcie, usunięte z list
internal_nodes.
append(Node(element_1, element_2, element_1.weight + element_2.weight)
return internal_nodes[0]
print(huffman({"a": 5, "b": 2, "c": 1, "d": 1, "r": 2}))
#11 0 -> #5 a 1 -> #6 0 -> #2 0 -> #1 c 1 -> #1 d 1 -> #4 0 -> #2 b 1 -> #2 r
Algorytm Huffmana tworzy drzewo prefiksów w czasie $O(|\Sigma| log|\Sigma|)$
Dowód: dominującą operacją jest posortowanie liter względem częstości występowania. Pozostałe oparacje mają złożoność liniową ze względu na liczbę liter.
from collections import defaultdict
def adaptive_huffman(text):
Node.nodes = []
count = defaultdict(int)
nodes = {"#": Node("#", weight=0)}
root = nodes["#"]
for letter in list(text):
if letter in nodes:
node = nodes[letter]
print(node.code() + ' ' + node.letter)
node.increment()
else:
updated_node = nodes["#"]
print(updated_node.code() + ' ' + updated_node.letter)
print("{0:b}".format(ord(letter)) + ' ' + letter)
node = Node(letter, parent=updated_node)
nodes[letter] = node
del nodes["#"]
zero_node = Node("#", parent=updated_node, weight=0)
updated_node.add_child(0, zero_node)
updated_node.add_child(1, node)
nodes["#"] = zero_node
updated_node.increment()
adaptive_huffman('abracadabra')
#0(-1,None) # # 1100001 a 0 # 1100010 b 00 # 1110010 r 0 a 100 # 1100011 c 0 a 1100 # 1100100 d 0 a 110 b 110 r 0 a
Niech $T$ oznacza kompletne, binarne, wagowane drzewo (z $n$ liśćmi), w których liście mają dodatni koszt, a koszt dowolnego węzła wewnętrznego stanowi koszt jego dzieci. Wtedt $T$ jest drzewem Huffmana wtedy i tylko wtedy, gdy jego liście mogą zostać uporządkowane w następujący sposób:
Optymalna kompresja ze zmienną długościa bloku musi uwzględnić 3 zagadnienia:
Optymalna kompresja ze zmienną długością bloku jest problemem NP-zupełnym.
Dopasuj zawsze najdłuższe słowo występujące w słowniku.
Strategia semi-zachłanna jest optymalna, jeśli $F$ zawiera wszystkie swoje prefiksy.
from math import ceil
def lempel_ziv_welch(text):
prefixes = []
while(len(text) > 0):
prefix = max_prefix(text, prefixes)
letter = text[len(prefix)]
# wypisz indeks prefix-u na ceil(log2(len(prefixes))) bitach
# wypisz początkowy kod letter na ceil(log(len(alphabet))) bitach
prefixes.append(prefix + letter)
text= text[len(prefix)+1:]